/**
 *    @file       caes.cpp
 *
 *    @brief      
 *    @details
 *
 *    @version    1.0
 *    @date       01/23/2014 10:29:18 AM
 *
 *    @author     Theophilus (), wangtf418@gmail.com
 */
#include "caes.h"

caes::caes() : m_ifset_enkey(0), m_ifset_dekey(0)
{

}

caes::~caes()
{

}

bf_int_t caes::set_encryptkey(const bf_uchar_t *key, bf_aes_keylen_t keylen)
{
	if (!key) 
	{
		return BF_ERROR;
	}

	AES_set_encrypt_key(key, keylen * 8, &m_encrypt_key);
	m_ifset_enkey = 1;
	return BF_OK;
}

bf_int_t caes::set_decryptkey(const bf_uchar_t *key, bf_aes_keylen_t keylen)
{
	if (!key) 
	{
		return BF_ERROR;
	}

	AES_set_encrypt_key(key, keylen * 8, &m_decrypt_key);
	m_ifset_dekey = 1;
	return BF_OK;
}

bf_int_t caes::encrypt(const bf_uchar_t input[16], bf_uchar_t output[16])
{
	if (m_ifset_enkey || !input || !output)
	{
		return BF_ERROR;
	}
	AES_encrypt(input, output, &m_encrypt_key);
	return BF_OK;
}

bf_int_t caes::decrypt(const bf_uchar_t input[16], bf_uchar_t output[16])
{
	if (m_ifset_dekey || !input || !output)
	{
		return BF_ERROR;
	}
	AES_decrypt(input, output, &m_decrypt_key);
	return BF_OK;
}

bf_int_t caes::encrypt(const bf_uchar_t *key, bf_aes_keylen_t keylen, const bf_uchar_t input[16], bf_uchar_t output[16])
{
	if (!key || !input || !output)
	{
		return BF_ERROR;
	}
	AES_KEY encrypt_key;
	AES_set_encrypt_key(key, keylen * 8, &encrypt_key);
	AES_encrypt(input, output, &encrypt_key);
	return BF_OK;
}

bf_int_t caes::decrypt(const bf_uchar_t *key, bf_aes_keylen_t keylen, const bf_uchar_t input[16], bf_uchar_t output[16])
{
	if (!key || !input || !output)
	{
		return BF_ERROR;
	}
	AES_KEY decrypt_key;
	AES_set_decrypt_key(key, keylen * 8, &decrypt_key);
	AES_decrypt(input, output, &decrypt_key);
	return BF_OK;
}

bf_int_t caes::encrypt(const bf_uchar_t *key, bf_aes_keylen_t keylen, const bf_uchar_t *data, bf_size_t len, std::string &str_out)
{
	if (!key || !data || len == 0)
	{
		return BF_ERROR;
	}

	AES_KEY aes_key;
	AES_set_encrypt_key(key, keylen * 8, &aes_key);

	bf_uchar_t inbuf[BF_AES_BLOCK_LEN + 1];
	bf_uchar_t outbuf[BF_AES_BLOCK_LEN + 1];
	bf_size_t offset = 0;

	str_out.clear();
	str_out.reserve(len + BF_AES_BLOCK_LEN);

	while (len - offset > 0)
	{
		if (len - offset >= BF_AES_BLOCK_LEN)
		{
			memcpy(inbuf, data + offset, BF_AES_BLOCK_LEN);
			AES_encrypt(inbuf, outbuf, &aes_key);
			str_out.append((const char*)outbuf, BF_AES_BLOCK_LEN);
			offset += BF_AES_BLOCK_LEN;
		}
		else
		{
			memset(inbuf, 0, BF_AES_BLOCK_LEN + 1);
			bf_size_t rest_len = len - offset;
			if (rest_len > 0)
			{
				memcpy(inbuf, data + offset, rest_len);
				AES_encrypt(inbuf, outbuf, &aes_key);
				str_out.append((const char*)outbuf, BF_AES_BLOCK_LEN);
				offset += rest_len;
			}
			break;
		}
	}

	return BF_OK;
}

bf_int_t caes::decrypt(const bf_uchar_t *key, bf_aes_keylen_t keylen, const bf_uchar_t *data, bf_size_t len, std::string &str_out)
{
	if (!key || !data || len == 0)
	{
		return BF_ERROR;
	}

	if ((len % BF_AES_BLOCK_LEN) != 0)
	{
		return BF_ERROR;
	}

	AES_KEY aes_key;
	AES_set_decrypt_key(key, keylen * 8, &aes_key);

	bf_uchar_t inbuf[BF_AES_BLOCK_LEN + 1];
	bf_uchar_t outbuf[BF_AES_BLOCK_LEN + 1];
	bf_size_t offset = 0;
	str_out.clear();
	str_out.reserve(len);

	while (len - offset > 0)
	{
		memcpy(inbuf, data + offset, BF_AES_BLOCK_LEN);
		AES_decrypt(inbuf, outbuf, &aes_key);
		str_out.append((const char*)outbuf, BF_AES_BLOCK_LEN);
		offset += BF_AES_BLOCK_LEN;
	}

	return BF_OK;
}

